;
; defines ast-manipulation routines
; the most important are:
;     process-ast-list
;     def-ast-handler


(defparameter *module-ast-mappings* (make-hash-table))

;
; defines a base class used in order to represent error information
;
(define-condition ast-error (error)
  (
   (text :initarg :text :reader text)
   ))

;
; defines "exception" class that is used in order to represent
; errors in filling malformed mapping's information
;
(define-condition multiple-definition-error (ast-error)
  (
   (entity :initarg :entity :reader entity)
   ))

;
; defines exception that is used in order to represent
; malformed type information
;
(define-condition malformed-type-error (ast-error)
  (
   (type-spec :initarg :type-spec :reader type-spec)
   ))

(defun signal-multiple-definition (arg)
  (if *allow-multiple-definitions*
      (format t "Warning: symbol ~a is defined twice~%" arg)
      (error 'multiple-definition-error
	     :text "symbol is already defined" :entity arg)
      ))

;
; maps function to symbol
;
(defmacro add-mapping (symbol function)
  `(progn
    (check-type ,symbol symbol)
    (check-type ,function function)
    (if (gethash ,symbol *module-ast-mappings*)
	(signal-multiple-definition ,symbol))
    (setf (gethash ,symbol *module-ast-mappings*) ,function))
  )

(defun transform-ast-arg-list (arg-list)
  (mapcar #'(lambda (sym)
	      (cond
		((listp sym) (append '(list) (transform-ast-arg-list sym)))
		((symbolp sym) (list 'quote sym))
		(t sym)
		))
	  arg-list))

(defun process-ast-list (ast-context ast-list)
  (let ((ast-handler (gethash (first ast-list) *module-ast-mappings*)))
    (unless ast-handler (error "ast-handler is omitted"))

    ; catch errors while evaluating ast functions
    (handler-case
	(eval (append
	       (list 'funcall ast-handler ast-context)
	       (transform-ast-arg-list (cdr ast-list))
	       ))
      (error (error-instance)
	(format t
		"error while evaluating ast function ~a at location ~a~%"
		(first ast-list)
		"TODO: implement location")
	;(describe error-instance)

	; throw the same error again
	(error error-instance)
	)
      )
    ))

(defmacro def-ast-handler (ast-name args &rest body)
  `(add-mapping ',ast-name
    (lambda ,args
      ;(format t "ast-handler~%")
      ,@body))
  )


(defstruct (ast-printing-context)
  (ostream :read-only :type stream)
  (indent "" :type string)
  )